
package com.license.logic.pso;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.xml.sax.SAXException;

import com.license.common.util.Constants;
import com.license.logic.behaviorlog.RiskCalculation;
import com.license.logic.behaviorlog.RiskFactor;
import com.license.logic.dao.ActivityDao;
import com.license.logic.dao.NutrionDAO;
import com.license.logic.geneticAlgorithm.common.CommonOperation;
import com.license.logic.preference.Activity;

public class ParticleSwarmOptimization {

	private double particleListSize;
	private double iterationNumber;
	private double paretoOptimalMethod;
	private CommonOperation commonOperation;
	
	private ActivityDao activityDao = new ActivityDao();
	private NutrionDAO nutrionDAO = new NutrionDAO();

	private static Map<String, Double> difference;
	private RiskFactor riskFactor;
	private Operation operation;
	private int iterationNumberr = 3;

	private Position globalPosition;
	
	public ParticleSwarmOptimization() {
		
	}
	
	public ParticleSwarmOptimization(String path, double particleListSize,
			double iterationNumber, double paretoOptimalMethod) throws ParserConfigurationException, SAXException, IOException {
		
		this.particleListSize = particleListSize;
		this.iterationNumber = iterationNumber;
		this.paretoOptimalMethod = paretoOptimalMethod;
		
		this.operation = new Operation();
		commonOperation = new CommonOperation();
		RiskCalculation riskCalculation = new RiskCalculation();
		this.riskFactor = riskCalculation.getRiskFactorsFromLog(path);
		this.operation.initializeDocumentBuilder(path);
		this.operation.setExceptedFoods(nutrionDAO.getExceptedFoods());
		this.operation.setPreferencedActivities(this.operation.getPreferencedActivitiesFromPath(activityDao.getAllActivities()));
		this.operation.setPreferencedFoods( this.operation.getPreferencedFoodsFromPath(nutrionDAO.getAllFoods()));
		this.operation.setAllFoods(nutrionDAO.getAllFoods());

		try {
			this.difference = this.operation.preprocessing(riskFactor);
		} catch (DOMException | ParserConfigurationException | SAXException
				| IOException | ParseException e) {
			e.printStackTrace();
		}

		double regularyDailyKalory = this.riskFactor.getAdditional()
				.getNutriotionKalory()
				- this.riskFactor.getAdditional().getAcitivityKalory();
		this.operation.setDesireKalory(this.operation.findKaloryNumber(
				difference.get("weight"), 1, regularyDailyKalory));
		this.operation.setDesireSbp(this.riskFactor.getSbp() + this.difference.get("sbp"));  // este sbp-ul la care vrem sa ajungem

		System.out.println(" calories per day to gain desire weight: "
				+ this.operation.getDesireKalory());
		System.out.println(this.difference);
		
	}
	
	public List<Activity> getPreferencePhysicalActivities(){
		List<Activity> activities = new ArrayList<Activity>();
		
		return activities;
	}

	public List<Particle> initialize(double particleListSize) throws DOMException,
			SAXException, IOException, ParserConfigurationException,
			ParseException {

		List<Particle> resultParticles = new ArrayList<Particle>();
		for (int i = 0; i < particleListSize; i++) {

			Particle particle = new Particle();
			Position position = new Position();
			Velocity velocity = new Velocity();

			position.setActivities(this.operation.generateRandomActivities(this.riskFactor
					.getWeight()));
			position.setAlimentation(this.operation.generateRandomAlimentation(Constants.JESS_CONSTRAINT_METHOD));
			position.setWeight(this.riskFactor.getWeight());
			velocity.setActivities(this.operation.generateRandomActivities(this.riskFactor
					.getWeight()));
			velocity.setAlimentation(this.operation.generateRandomAlimentation(Constants.JESS_CONSTRAINT_METHOD));

			particle.setPosition(position);
			particle.setVelocity(velocity);

			particle.setLocalBest(this.operation.clonePosition(particle
					.getPosition()));
			resultParticles.add(particle);
		}

		return resultParticles;
	}

	public List<Particle> particleSwarmOptimization(double c1, double c2, double c3) throws DOMException, SAXException,
			IOException, ParserConfigurationException, ParseException {
		
		List<Particle> particles = new ArrayList<Particle>();
		for (int day = 0 ; day < Constants.DIET_NUMBER_GENERATED ; day++){
			Particle bestIndivid = new Particle();
			this.globalPosition = null;

			List<Particle> initializedParticles = initialize(particleListSize);
			int iteration = 0;
			do {
				for (Particle particle : initializedParticles) {
					// update the particle best postion
					if (this.operation.fitness(particle.getPosition().getAllCalories(), paretoOptimalMethod, this.riskFactor.getSbp()) < this.operation.fitness(
							particle.getLocalBest().getAllCalories(), paretoOptimalMethod, this.riskFactor.getSbp())) {
						particle.setLocalBest(this.operation.clonePosition(particle
								.getPosition()));
					}
					// update the global best postion
					if (this.operation.fitness(particle.getLocalBest().getAllCalories(), paretoOptimalMethod, this.riskFactor.getSbp()) < this.operation.fitness(
							this.globalPosition != null ? this.globalPosition.getAllCalories() : 0, paretoOptimalMethod, this.riskFactor.getSbp())) {
						bestIndivid = particle;
						this.globalPosition = this.operation.clonePosition(particle
								.getLocalBest());
					}
				}

				for (Particle particle : initializedParticles) {

					Position position = operation.updatePosition(particle, day);
					Velocity velocity = operation.updateVelocity(particle, day, this.globalPosition, c1, c2, c3);
					particle.setPosition(position);
					particle.setVelocity(velocity);

				}

				iteration = iteration + 1;
			} while (iteration < iterationNumberr);

			particles.add(bestIndivid);
		}
		System.out.println("desire calory : " + this.operation.getDesireKalory());
		System.out.println("desire sbp : " + this.operation.getDesireSbp());
		difference.put("desireCalory", operation.getDesireKalory());
		difference.put("desireSbp", operation.getDesireSbp());
		return particles;
	}

	public Position getGlobalPosition() {
		return globalPosition;
	}

	public void setGlobalPosition(Position globalPosition) {
		this.globalPosition = globalPosition;
	}
	
	public Map<String, Double> getDifferenceFromPreprocessing(){

		return difference;
	}

}
